<?php
/**
 * BUtils class.
 * BUtils is the helper static class for business portal.
 * 
 * Yawii Business 版权所有 @ 2012.
 */
class BUtils {
	/**
	 * GKEY is a global encryption key = yamii.fi. DON'T CHANGE!!!
	 */
	const GKEY = 'yamii.fi';
	
	/**
	 * Return a shorten url.
	 * @param string $url
	 */
	public static function shortenUrl($url) {
		//自定义生成MD5加密字符串的混合KEY
		$key = '';
		//对传入网址进行MD5加密
		$md5hex = md5($url . $key);
		//要使用生成URL的字符
		$hash = sprintf('%u', crc32($md5hex));
		
		return self::encode($hash);
	}
	
    public static function encode($number, $charsets = 'uppercase') {
		if ($charsets === 'uppercase') $chars = '123456789ABCDEFGHJKLMNPQRSTUXZ';
		else if ($charsets === 'lowercase') $chars = '123456789abcdefghijkmnpqrstuxz';
		else $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
		
		if(!isset($number)){
			return null;
		}

		$base = strlen($chars);
		$out = "";
		for ($t = floor(log($number, $base)); $t >= 0; $t--) {
			$bcp = bcpow($base, $t);
			$a   = floor($number / $bcp) % $base;
			$out = $out . substr($chars, $a, 1);
			$number  = $number - ($a * $bcp);
		}
		$out = strrev($out);
		return $out;
    }

    public static function decode($string, $charsets = 'uppercase'){
		if ($charsets === 'uppercase') $chars = '123456789ABCDEFGHJKLMNPQRSTUXZ';
		else if ($charsets === 'lowercase') $chars = '123456789abcdefghijkmnpqrstuxz';
		else $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
		
		$base = strlen($chars);
		$string  = strrev($string);
		$out = 0;
		$len = strlen($string) - 1;

		for ($t = 0; $t <= $len; $t++){
			$bcpow = bcpow($base, $len - $t);
			$out   = $out + strpos($chars, substr($string, $t, 1)) * $bcpow;
		}

		$out = sprintf('%F', $out);
		$out = substr($out, 0, strpos($out, '.'));
		return $out;
    }
	
	/**
	 * get cookie with array
	 * @param string $key 
	 */
	public static function getCookie($key) {
		if (isset(Yii::app()->request->cookies[$key])) {
			$string_value = Yii::app()->request->cookies[$key]->value;
			return json_decode($string_value, true);
		} else {
			return null;
		}
	}
	
	/**
	 * set cookie with array
	 * @param string $key
	 * @param array $value 
	 */
	public static function setCookie($key, $value) {
		$value_json_string = json_encode($value);
		$cookie = new CHttpCookie($key, $value_json_string);
		$cookie->path = "/";
		$cookie->expire = time() + 3600*24*180;  // keep 180 days
		Yii::app()->request->cookies[$key] = $cookie;
	}
	
	/**
	 * clear the cookie.
	 * @param type $key 
	 */
	public static function clearCookie($key) {
		unset(Yii::app()->request->cookies[$key]);
	}
	
	/**
	 *
	 * @param type $url
	 * @param type $query
	 */
	public static function appendQueryUrl($url, $query) {
		if (strpos($url, '?') === false) {
			return $url . '?' . $query;
		} else {
			return $url . '&' . $query;
		}
	}
	
	/**
	 * pack abbreviation/language array
	 *
	 * important note: you must have the default language as the last item in each major language, after all the
	 * en-ca type entries, so en would be last in that case
	 * 
	 * @return array
	 */
	public static function Languages()
	{
		return array(
			'af' => 'Afrikaans',
			'sq' => 'Albanian',
			'ar-dz' => 'Arabic (Algeria)',
			'ar-bh' => 'Arabic (Bahrain)',
			'ar-eg' => 'Arabic (Egypt)',
			'ar-iq' => 'Arabic (Iraq)',
			'ar-jo' => 'Arabic (Jordan)',
			'ar-kw' => 'Arabic (Kuwait)',
			'ar-lb' => 'Arabic (Lebanon)',
			'ar-ly' => 'Arabic (libya)',
			'ar-ma' => 'Arabic (Morocco)',
			'ar-om' => 'Arabic (Oman)',
			'ar-qa' => 'Arabic (Qatar)',
			'ar-sa' => 'Arabic (Saudi Arabia)',
			'ar-sy' => 'Arabic (Syria)',
			'ar-tn' => 'Arabic (Tunisia)',
			'ar-ae' => 'Arabic (U.A.E.)',
			'ar-ye' => 'Arabic (Yemen)',
			'ar' => 'Arabic',
			'hy' => 'Armenian',
			'as' => 'Assamese',
			'az' => 'Azeri',
			'eu' => 'Basque',
			'be' => 'Belarusian',
			'bn' => 'Bengali',
			'bg' => 'Bulgarian',
			'ca' => 'Catalan',
			'zh-cn' => 'Chinese (China)',
			'zh-hk' => 'Chinese (Hong Kong SAR)',
			'zh-mo' => 'Chinese (Macau SAR)',
			'zh-sg' => 'Chinese (Singapore)',
			'zh-tw' => 'Chinese (Taiwan)',
			'zh' => 'Chinese',
			'hr' => 'Croatian',
			'cs' => 'Czech',
			'da' => 'Danish',
			'div' => 'Divehi',
			'nl-be' => 'Dutch (Belgium)',
			'nl' => 'Dutch (Netherlands)',
			'en-au' => 'English (Australia)',
			'en-bz' => 'English (Belize)',
			'en-ca' => 'English (Canada)',
			'en-ie' => 'English (Ireland)',
			'en-jm' => 'English (Jamaica)',
			'en-nz' => 'English (New Zealand)',
			'en-ph' => 'English (Philippines)',
			'en-za' => 'English (South Africa)',
			'en-tt' => 'English (Trinidad)',
			'en-gb' => 'English (United Kingdom)',
			'en-us' => 'English (United States)',
			'en-zw' => 'English (Zimbabwe)',
			'en' => 'English',
			'us' => 'English (United States)',
			'et' => 'Estonian',
			'fo' => 'Faeroese',
			'fa' => 'Farsi',
			'fi' => 'Finnish',
			'fr-be' => 'French (Belgium)',
			'fr-ca' => 'French (Canada)',
			'fr-lu' => 'French (Luxembourg)',
			'fr-mc' => 'French (Monaco)',
			'fr-ch' => 'French (Switzerland)',
			'fr' => 'French (France)',
			'mk' => 'FYRO Macedonian',
			'gd' => 'Gaelic',
			'ka' => 'Georgian',
			'de-at' => 'German (Austria)',
			'de-li' => 'German (Liechtenstein)',
			'de-lu' => 'German (Luxembourg)',
			'de-ch' => 'German (Switzerland)',
			'de' => 'German (Germany)',
			'el' => 'Greek',
			'gu' => 'Gujarati',
			'he' => 'Hebrew',
			'hi' => 'Hindi',
			'hu' => 'Hungarian',
			'is' => 'Icelandic',
			'id' => 'Indonesian',
			'it-ch' => 'Italian (Switzerland)',
			'it' => 'Italian (Italy)',
			'ja' => 'Japanese',
			'kn' => 'Kannada',
			'kk' => 'Kazakh',
			'kok' => 'Konkani',
			'ko' => 'Korean',
			'kz' => 'Kyrgyz',
			'lv' => 'Latvian',
			'lt' => 'Lithuanian',
			'ms' => 'Malay',
			'ml' => 'Malayalam',
			'mt' => 'Maltese',
			'mr' => 'Marathi',
			'mn' => 'Mongolian (Cyrillic)',
			'ne' => 'Nepali (India)',
			'nb-no' => 'Norwegian (Bokmal)',
			'nn-no' => 'Norwegian (Nynorsk)',
			'no' => 'Norwegian (Bokmal)',
			'or' => 'Oriya',
			'pl' => 'Polish',
			'pt-br' => 'Portuguese (Brazil)',
			'pt' => 'Portuguese (Portugal)',
			'pa' => 'Punjabi',
			'rm' => 'Rhaeto-Romanic',
			'ro-md' => 'Romanian (Moldova)',
			'ro' => 'Romanian',
			'ru-md' => 'Russian (Moldova)',
			'ru' => 'Russian',
			'sa' => 'Sanskrit',
			'sr' => 'Serbian',
			'sk' => 'Slovak',
			'ls' => 'Slovenian',
			'sb' => 'Sorbian',
			'es-ar' => 'Spanish (Argentina)',
			'es-bo' => 'Spanish (Bolivia)',
			'es-cl' => 'Spanish (Chile)',
			'es-co' => 'Spanish (Colombia)',
			'es-cr' => 'Spanish (Costa Rica)',
			'es-do' => 'Spanish (Dominican Republic)',
			'es-ec' => 'Spanish (Ecuador)',
			'es-sv' => 'Spanish (El Salvador)',
			'es-gt' => 'Spanish (Guatemala)',
			'es-hn' => 'Spanish (Honduras)',
			'es-mx' => 'Spanish (Mexico)',
			'es-ni' => 'Spanish (Nicaragua)',
			'es-pa' => 'Spanish (Panama)',
			'es-py' => 'Spanish (Paraguay)',
			'es-pe' => 'Spanish (Peru)',
			'es-pr' => 'Spanish (Puerto Rico)',
			'es-us' => 'Spanish (United States)',
			'es-uy' => 'Spanish (Uruguay)',
			'es-ve' => 'Spanish (Venezuela)',
			'es' => 'Spanish (Traditional Sort)',
			'sx' => 'Sutu',
			'sw' => 'Swahili',
			'sv-fi' => 'Swedish (Finland)',
			'sv' => 'Swedish',
			'syr' => 'Syriac',
			'ta' => 'Tamil',
			'tt' => 'Tatar',
			'te' => 'Telugu',
			'th' => 'Thai',
			'ts' => 'Tsonga',
			'tn' => 'Tswana',
			'tr' => 'Turkish',
			'uk' => 'Ukrainian',
			'ur' => 'Urdu',
			'uz' => 'Uzbek',
			'vi' => 'Vietnamese',
			'xh' => 'Xhosa',
			'yi' => 'Yiddish',
			'zu' => 'Zulu'
		);
	}
	
	/**
	 * Return all countries array.
	 * @param string $continent - Africa, Antarctica, Asia, Australia, Europe, North America, South America
	 * @return array (key => value) 
	 */
	public static function Countries($continent) {
		$Europe = array(
			'AL' => 'Albania',
			'AD' => 'Andorra',
			'AM' => 'Armenia',
			'AT' => 'Austria',
			'BY' => 'Belarus',
			'BE' => 'Belgium',
			'BA' => 'Bosnia and Herzegovina',
			'BG' => 'Bulgaria',
			'HR' => 'Croatia',
			'CY' => 'Cyprus',
			'CZ' => 'Czech Republic',
			'DK' => 'Denmark',
			'EE' => 'Estonia',
			'FO' => 'Faeroe Islands',
			'FI' => 'Finland',
			'FR' => 'France',
			'GE' => 'Georgia',
			'DE' => 'Germany',
			'GI' => 'Gibraltar',
			'GR' => 'Greece',
			'HU' => 'Hungary',
			'IS' => 'Iceland',
			'IE' => 'Ireland',
			'IT' => 'Italy',
			'LV' => 'Latvia',
			'LT' => 'Lithuania',
			'LU' => 'Luxembourg',
			'MK' => 'Macedonia',
			'MT' => 'Malta',
			'MC' => 'Monaco',
			'NL' => 'Netherlands',
			'NO' => 'Norway',
			'PO' => 'Poland',
			'PT' => 'Portugal',
			'RO' => 'Romania',
			'RU' => 'Russian Federation',
			'SM' => 'San Marino',
			'SK' => 'Slovakia',
			'SI' => 'Slovenia',
			'ES' => 'Spain',
			'SE' => 'Sweden',
			'CH' => 'Switzerland',
			'TR' => 'Turkey',
			'UA' => 'Ukraine',
			'GB' => 'United Kingdom',
			'VA' => 'Vatican City State',
		);
		switch ($continent) {
			case 'Europe':
				return $Europe;
				break;
			default:
				return $Europe;
				break;
		}
	}
	
	/**
	 * This method is used to get country name.
	 * @param string $currency 'EUR|USD'
	 */
	public static function GetCountry($country_code) {
	    $list = self::Countries('Europe');
		return $list[$country_code];
	}
    
    /**
	 * Returns all cities of given country.
	 * @param string $country_code
	 * @return array 
	 */
    public static function Cities($country_code = 'FI') {
		$Finland = array(
			'Helsinki' => 'Helsinki',
			'Espoo' => 'Espoo',
			'Vantaa' => 'Vantaa',
			'Tampere' => 'Tampere',
			'Turku' => 'Turku',
			'Oulu' => 'Oulu',
			'Hyvinkää' => 'Hyvinkää',
			'Vaasa' => 'Vaasa',
			'Jyväskylä' => 'Jyväskylä',
			'Lahti' => 'Lahti',
			'Lappeenranta' => 'Lappeenranta',
			'Mikkeli' => 'Mikkeli',
			'Hämeenlinna' => 'Hämeenlinna',
			'Järvenpää' => 'Järvenpää',
			'Joensuu' => 'Joensuu',
			'Kaarina' => 'Kaarina',
			'Kajaani' => 'Kajaani',
			'Kerava' => 'Kerava',
			'Kirkkonummi' => 'Kirkkonummi',
			'Kokkola' => 'Kokkola',
			'Kotka' => 'Kotka',
			'Kouvola' => 'Kouvola',
			'Kuopio' => 'Kuopio',
			'Lohja' => 'Lohja',
			'Nokia' => 'Nokia',
			'Nurmijärvi' => 'Nurmijärvi',
			'Pori' => 'Pori',
			'Porvoo' => 'Porvoo',
			'Rauma' => 'Rauma',
			'Rovaniemi' => 'Rovaniemi',
			'Riihimäki' => 'Riihimäki',
			'Salo' => 'Salo',
			'Seinäjoki' => 'Seinäjoki',
			'Tuusula' => 'Tuusula',
			'Ylöjärvi' => 'Ylöjärvi',
			'Suomi' => 'Suomi',
		);
		switch ($country_code) {
			case 'FI':
				return $Finland;
				break;
			default:
				return $Finland;
				break;
		}
    }
	
	/**
	 * Get category array.
	 */
	public static function Categories() {
		return array(
			'food,drinks' => 'Food & Drinks',
			'retail,shopping' => 'Retail & Shopping',
			'health,beauty' => 'Health & Beauty',
			'sports,recreation' => 'Sports & Recreation',
			'travel,hotel' => 'Travel & Hotel',
			'electronics,appliances' => 'Electronics & Appliances',
			'labor,services' => 'Labor & Services',
			'others' => 'Others',
		);
	}
	
	/**
	 * Return speical redeem status array. 
	 */
	public static function RedeemedStatus() {
		return array(
			0 => BUtils::T('unredeemed_lbl'),
			1 => BUtils::T('special_redeemed_lbl'),
		);
	}
	
	/**
	 * Return speical's status array. 
	 */
	public static function SpecialStatus() {
		return array(
			0 => BUtils::T('unpublished_lbl'),
			1 => BUtils::T('published_lbl'),
		);
	}
	
	/**
	 * Return dish's status array. 
	 */
	public static function DishStatus() {
		return array(
			0 => BUtils::T('unsubmitted_lbl'),
			1 => BUtils::T('submitted_lbl'),
			2 => BUtils::T('published_lbl'),
		);
	}
	
	/**
	 * Return speical's valid time status array. 
	 */
	public static function SpecialValidStatus() {
		return array(
			3 => BUtils::T('not_completed_lbl'),
			0 => BUtils::T('special_active_lbl'),
			1 => BUtils::T('special_pending_lbl'),
			2 => BUtils::T('special_completed_lbl'),
		);
	}
	
	/**
	 * Get the recent day name: today, tomorrow, Tus.
	 * @param string $weekdays 1,2,3,4,5,6,7
	 * @return string
	 */
	public static function GetRecentDayName($weekdays, $week_string, $next24, $start_hour) {
		$today_lbl = BUtils::T('today_lbl');
		$tomorrow_lbl = BUtils::T('tomorrow_lbl');
		
		$today_weekno = date('N');
		$tomorrow_weekno = $today_weekno + 1;
		if ($today_weekno == 7) $tomorrow_weekno = 1;
		
		if ($next24) {
			if ($start_hour > date('Hi')) {// the start hour is coming
				$ipos = strpos($weekdays, $today_weekno);
				if ($ipos !== false) {
					return $today_lbl;//find today
				} else {
					$ipos = strpos($weekdays, (string)$tomorrow_weekno);
					if ($ipos !== false) {
						return $tomorrow_lbl;//find tomorrow
					}
				}
			} else {
				$ipos = strpos($weekdays, (string)$tomorrow_weekno);
				if ($ipos !== false) {
					return $tomorrow_lbl;//find tomorrow
				}
			}
		} else {
			$ipos = strpos($weekdays, $today_weekno);
			if ($ipos !== false) {
				return $today_lbl;//find today
			} else {
				$ipos = strpos($weekdays, (string)$tomorrow_weekno);
				if ($ipos !== false) {
					return $tomorrow_lbl;//find tomorrow
				}
			}
		}
		//return week days string
		return $week_string;
	}
	
	/**
	 * Return string with weekdays.
	 * @param string $weekdays 1,2,3 | 1,2,3,4,5 | 3,4 | 5
	 */
	public static function GetWeekdays($weekdays, $week_array) {
		$array = explode(',', $weekdays);
		
		//only one day here
		if (count($array) == 1) {
			if (isset($week_array[$array[0]])) {
				return $week_array[$array[0]];
			} else {
				return null;
			}
		}
		
		//check if there is no break day
		$isbreak = false;
		$begin = $array[0];
		foreach ($array as $day) {
			if ($begin != $day) {
				$isbreak = true;
				break;
			}
			$begin ++;
		}
		$selected_days = null;
		if ($isbreak) {
			foreach ($array as $day) {
				if (isset($week_array[$day])) $selected_days .= "{$week_array[$day]} ";
			}
		} else {
			$selected_days = $week_array[$array[0]] . ' - ' . $week_array[$array[count($array)-1]];
		}
		
		return trim($selected_days);
	}
	
	/**
	 * Get week day keys.
	 */
	public static function WeekDayKeys($number = 7, $abbr = false) {
		$postfix = $abbr ? '_b' : '';
		
		if ($abbr) {
			return array(
				'1' => $GLOBALS['LNG']['week_1_lbl'],
				'2' => $GLOBALS['LNG']['week_2_lbl'],
				'3' => $GLOBALS['LNG']['week_3_lbl'],
				'4' => $GLOBALS['LNG']['week_4_lbl'],
				'5' => $GLOBALS['LNG']['week_5_lbl'],
				'6' => $GLOBALS['LNG']['week_6_lbl'],
				'7' => $GLOBALS['LNG']['week_7_lbl'],
			);
		}
		
		if ($number == 7) {
			return array(
				'1' => $GLOBALS['LNG']['monday_lbl' . $postfix],
				'2' => $GLOBALS['LNG']['tuesday_lbl' . $postfix],
				'3' => $GLOBALS['LNG']['wednesday_lbl' . $postfix],
				'4' => $GLOBALS['LNG']['thursday_lbl' . $postfix],
				'5' => $GLOBALS['LNG']['friday_lbl' . $postfix],
				'6' => $GLOBALS['LNG']['saturday_lbl' . $postfix],
				'7' => $GLOBALS['LNG']['sunday_lbl' . $postfix],
			);
		} else {
			return array(
				'1' => $GLOBALS['LNG']['monday_lbl' . $postfix],
				'2' => $GLOBALS['LNG']['tuesday_lbl' . $postfix],
				'3' => $GLOBALS['LNG']['wednesday_lbl' . $postfix],
				'4' => $GLOBALS['LNG']['thursday_lbl' . $postfix],
				'5' => $GLOBALS['LNG']['friday_lbl' . $postfix],
			);
		}
	}
	
	public static function AllHour24() {
		return array(
			'0' => '0:00',
			'30' => '0:30',
			'100' => '1:00',
			'130' => '1:30',
			'200' => '2:00',
			'230' => '2:30',
			'300' => '3:00',
			'330' => '3:30',
			'400' => '4:00',
			'430' => '4:30',
			'500' => '5:00',
			'530' => '5:30',
			'600' => '6:00',
			'630' => '6:30',
			'700' => '7:00',
			'730' => '7:30',
			'800' => '8:00',
			'830' => '8:30',
			'900' => '9:00',
			'930' => '9:30',
			'1000' => '10:00',
			'1030' => '10:30',
			'1100' => '11:00',
			'1130' => '11:30',
			'1200' => '12:00',
			'1230' => '12:30',
			'1300' => '13:00',
			'1330' => '13:30',
			'1400' => '14:00',
			'1430' => '14:30',
			'1500' => '15:00',
			'1530' => '15:30',
			'1600' => '16:00',
			'1630' => '16:30',
			'1700' => '17:00',
			'1730' => '17:30',
			'1800' => '18:00',
			'1830' => '18:30',
			'1900' => '19:00',
			'1930' => '19:30',
			'2000' => '20:00',
			'2030' => '20:30',
			'2100' => '21:00',
			'2130' => '21:30',
			'2200' => '22:00',
			'2230' => '22:30',
			'2300' => '23:00',
			'2330' => '23:30',
		);
	}
	
	/**
	 * Get a standard address like "Alex street 18, 26520 Helsinki".
	 */
	public static function GetAddress($street, $zip = null, $city = null) {
		if (!empty($zip)) {
			if (!empty($city)) $street .= ', ' . $zip . ' ' . $city;
		} else {
			if (!empty($city)) $street .= ', ' . $city;
		}
		return $street;
	}
	
	/**
	 * Get a standard city like "26520 Helsinki".
	 */
	public static function GetZipCity($zip, $city) {
		if (empty($zip)) {
			return $city;
		} else {
			return $zip . ' ' . $city;
		}
	}
	
	/**
	 * Get the hour range format.
	 * @param string $startTime 1000
	 * @param string $endTime 1530
	 * @return string e.g. 10:00 - 15:30
	 */
	public static function GetHourRangeFormat($startTime, $endTime) {
		if (empty($startTime) and empty($endTime)) return null;
		$startTime = sprintf('%04d', $startTime);
		$endTime = sprintf('%04d', $endTime);
		
		if (!empty($startTime)) $startHour = substr($startTime, 0, 2) . ':' . substr($startTime, 2, 2);
		if (!empty($endTime)) $endHour = substr($endTime, 0, 2) . ':' . substr($endTime, 2, 2);
		
		if (empty($startHour)) return $endHour;
		else if (empty($endHour)) return $startTime;
		else return $startHour . ' - ' . $endHour;
	}
	
	/**
	 * Get the end hour format 15:30.
	 * @param string $endTime 1530
	 * @return string e.g. 15:30
	 */
	public static function GetEndHourFormat($endTime) {
		if (empty($endTime)) return null;
		$endTime = sprintf('%04d', $endTime);
		return substr($endTime, 0, 2) . ':' . substr($endTime, 2, 2);
	}
	
	/**
	 * Return date range string.
	 * @param int|timestamp $ts_start
	 * @param int|timestamp $ts_end
	 * @param string $format d.m.Y
	 * @return string 
	 */
	public static function GetDateRangeString($ts_start, $ts_end, $format = 'd.m.Y') {
		$returned_string = null;
		$start_date = date($format, $ts_start);
		$end_date = date($format, $ts_end);
		
		//if the same day
		if ($start_date == $end_date) {
			return $start_date;
		}
		
		//if not the same day
		if ($ts_start > 0 and $ts_end > 0) {
			if ($ts_start === $ts_end) $returned_string = $start_date;
			else $returned_string = $start_date . ' - ' . $end_date;
		} else if ($ts_start > 0) {
			$returned_string = $start_date;
		} else if ($ts_end > 0) {
			$returned_string = $end_date;
		}
		return $returned_string;
	}
	
	/**
	 * Get the standardized category key.
	 * @param string $category_key
	 * @return string
	 */
	public static function GetCategoryKey($category_key) {
		foreach (self::Categories() as $key => $value) {
			if (false !== strpos($key, $category_key)) {
				return $key;
			}
		}
		return 'no_categroy_key';
	}
	
	/**
	 * Get image file extension name.
	 * @param string $type image/gif | image/jpeg | image/png
	 */
	public static function GetImageExtension($type) {
		switch ($type) {
			case 'image/gif':
			case 'gif':
				return 'gif';
			case 'image/jpeg':
			case 'image/jpg':
				return 'jpg';
			case 'image/png':
				return 'png';
			default:
				return '';
		}
	}
	
	/**
	 * Get picture url with type key.
	 * @param string $url_picture 
	 */
	public static function GetPictureUrl($url_picture) {
		return empty($url_picture) ? Yii::app()->baseUrl . '/images/logo_default.png' : $url_picture;
	}
	
	/**
	 * Get thumb url from business site.
	 * @param string $url_picture
	 */
	public static function GetOfferThumbUrl($url_picture) {
		if (empty($url_picture)) {
			return Yii::app()->baseUrl . '/images/default_special_thumb.png';
		}
		return $url_picture;
	}
	
	/**
	 * Get picture url from business site.
	 * @param string $url_picture
	 */
	public static function GetOfferPictureUrl($url_picture) {
		if (empty($url_picture)) {
			return Yii::app()->baseUrl . '/images/default_special_picture.png';
		}
		return $url_picture;
	}
	
	/**
	 * Get image uploading folder. Create it if not exists.
	 * /webdir/upload/merchants/
	 */
	public static function GetUploadingFolder($subfolder = null) {
		$folder = Yii::app()->basePath . '/..' . Yii::app()->params['uploadPath'] . $subfolder . '/';
		if (!file_exists($folder)) {  //check the folder exists or not
			@mkdir($folder, 0777);    //create folder if not exists
		}
		return $folder;
	}
	
	/**
	 * Get image uploading Url:
	 * /webdir/upload/merchants/
	 */
	public static function GetUploadingUrl($subfolder = null) {
		return Yii::app()->baseUrl . Yii::app()->params['uploadUrl'] . $subfolder . '/';
	}
	
	/**
	 * Get facebook language code.
	 */
	public static function GetFacebookLanguage($lang = null) {
		if (empty($lang)) return 'en_GB';
		switch (strtolower($lang)) {
			case 'fi':
			case 'fi_fi':
				$code = 'fi_FI';
				break;
			case 'chs':
			case 'zh_cn':
				$code = 'zh_CN';
				break;
			case 'zh_tw':
				$code = 'zh_TW';
				break;
			default:
				if (strlen($lang) == 5) {
					$code = substr($lang, 0, 3) . strtoupper(substr($lang, 3, 2));
				} else {
					$code = 'en_GB';
				}
				break;
		}
		return $code;
	}
	
	/**
	 * Set global language data returned by browser.
	 */
	public static function SetLanguage($lang = null) {
		if (empty($lang)) $lang = Yii::app()->user->getState('lang');	        //get session language
		if (empty($lang)) $lang = BUtils::getCookie('lang');	                //get cookie language
		//if (empty($lang)) $lang = Yii::app()->request->getPreferredLanguage();	//get browser language
		
		if (empty($GLOBALS['LNG'])
			or (Yii::app()->language != $lang)) {
			// get default primary language, the first one in array that is
			$lang = str_replace('-', '_', $lang);
			switch (strtolower($lang)) {
				case 'en':
				case 'en_us':
				case 'en_gb':
					$lang = 'en_gb';
					$file = 'en_gb';
					break;
				case 'cn':
				case 'chs':
				case 'zh_cn':
					$lang = 'zh_cn';
					$file = 'zh_cn';
					break;
				default:
					$lang = 'fi_fi';
					$file = 'fi_fi';
					break;
			}
			$path = dirname(__FILE__) . '/../data/lang.'. $file .'.json';
			$path_default = dirname(__FILE__) . '/../data/lang.en_gb.json';
			$GLOBALS['LNG'] = json_decode(file_get_contents($path), true);
			$GLOBALS['LNG_DEFAULT'] = json_decode(file_get_contents($path_default), true);
		}
		
		//set language
		Yii::app()->setLanguage($lang);
		Yii::app()->user->setState('lang', $lang);
		BUtils::setCookie('lang', $lang);
		
		//set locale
		Yii::app()->localtime->Locale = Yii::app()->getLanguage();
		Yii::app()->localtime->TimeZone = Yii::app()->getTimeZone();
	}
	
	/**
	 * Created for strip tags (...) with sub string.
	 * @param string $string
	 * @param int $length
	 * @return string 
	 */
	public static function SubString($string, $length = null) {
		if ($length == null) $length = 80;
		$stringDisplay = substr(strip_tags($string), 0, $length);
		if (strlen(strip_tags($string)) > $length) $stringDisplay .= ' ...';
		return $stringDisplay;
	}
	
	/**
	 * Translate languages. Using the default if there is no such key.
	 * @param string $key
	 * @param array(key => value) $params
	 * @return string 
	 */
	public static function T($key, $params = null) {
		if (isset($GLOBALS['LNG'][$key])) {
			$raw = $GLOBALS['LNG'][$key];
		} else {
			$raw = $GLOBALS['LNG_DEFAULT'][$key];
		}
		if (empty($params)) {
			return $raw;
		}
		return str_replace(array_keys($params), array_values($params), $raw);
	}
	
	/**
	 * Get a 32-bit hash value.
	 * @param string $string	the original string
	 * @param string $key		the key added to the original string
	 * @return string
	 */
	public static function Hash32($string, $key = self::GKEY) {
		$security = new CSecurityManager();
		return md5($security->hashData($string, $key));
	}
	
	/**
	 * Encrypts a string.
	 * @return string A base64-string.
	 */
	public static function Encrypt($string, $key = self::GKEY) {
		$key = substr(md5($key), 0, 8);
		$iv = '12345678';
		$cipher = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '');
		mcrypt_generic_init($cipher, $key, $iv);
		$encrypted = mcrypt_generic($cipher, $string);
		mcrypt_generic_deinit($cipher);
		
		return base64_encode($encrypted);
	}
	
	/**
	 * Decrypts a base64-string.
	 */
	public static function Decrypt($string, $key = self::GKEY) {
		$key = substr(md5($key), 0, 8);
		$string = base64_decode($string);
		$iv = '12345678';
		$cipher = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '');
		mcrypt_generic_init($cipher, $key, $iv);
		$decrypted = mdecrypt_generic($cipher, $string);
		mcrypt_generic_deinit($cipher);
		
		return $decrypted;
	}

	/**
	 * Encrypts a string.
	 * @return string A base64-string.
	 */
	public static function EncryptOld($string, $key = self::GKEY) {
		$security = new CSecurityManager();
		return base64_encode($security->encrypt($string, $key));
	}
	
	/**
	 * Decrypts a base64-string.
	 */
	public static function DecryptOld($string, $key = self::GKEY) {
		$security = new CSecurityManager();
		return $security->decrypt(base64_decode($string), $key);
	}
	
	/**
	 * Filter array with a set of value
	 * 
	 * e.g.
	 * array_filter_values(array('a', 'b', 'c'), array('b', 'c')) => array('a')
	 */
	public static function array_filter_values($main_array, $sub_array) {
		foreach ($sub_array as $hit_value) {
			$main_array = array_filter($main_array, function ($main_item) use ($hit_value) {
				return ($main_item !== $hit_value);
			});
		}
		return $main_array;
	}
	
	/**
	 * for case insensitive array_search.
	 */
	public static function array_search_i($str, $array) {
		if (empty($array)) return false;
		foreach($array as $key => $value) {
			if (!empty($value)) {
				if (stristr($str, $value)) return $key;
			}
		}
		return false;
	}
	
	/**
	 * Search for containing a given array.
	 * @param string $needle
	 * @param array $array
	 * @param bool $sensitive=false if the search is case sensitive.
	 * @return mixed the key of array, or false if needle is not found.
	 */
	public static function array_contain($needle, $array, $sensitive = false) {
		if (empty($array)) return false;
		if ($sensitive) {
			foreach ($array as $key => $value) {
				if (!empty($value)) {
					if (false !== stristr($value, $needle)) {
						return $key;
					}
				}
			}
		} else {
			foreach ($array as $key => $value) {
				if (!empty($value)) {
					if (false !== strstr($value, $needle)) {
						return $key;
					}
				}
			}
		}
		
		return false;
	}
	
	/**
	 * Append an array to array.
	 * @param array $array (ref)
	 * @param array $appending (ref)
	 */
	public static function array_append(&$array, &$appending) {
		foreach ($appending as $item) {
			array_push($array, $item);
		}
	}
	
	/**
	 * Gets discount number
	 * @param float $ori_price 26.8
	 * @param float $new_price 18.8
	 * @param string $type savings | discounts
	 * @return string 89% | -39%
	 */
	public static function GetDiscount($ori_price, $new_price, $type = 'savings') {
		if ($ori_price>0) {
			$float = round(($new_price / $ori_price) * 100);
			if ($type == 'savings') {
				return 100 - $float;
			} else {
				return $float - 100;
			}
		} else {
			return null;
		}
	}
	
	/**
	 * Remove one key or multiply keys from the query string. (key=?)
	 * @param mixed $keys
	 * @param string $query - e.g. $_SERVER['QUERY_STRING']
	 * @return string 
	 */
	public static function RemoveQueryString($keys, $query) {
		if (empty($query) or empty($keys)) {
			return $query;
		}
		$query_array = explode('&', $query);
		for ($i = 0; $i < count($query_array); $i ++) {
			// find the keys, then delete
			if (is_array($keys)) {
				foreach ($keys as $k) {
					$pos = stripos($query_array[$i], $k.'=');
					if ($pos === 0) {
						$query_array[$i] = null;
					}
				}
			} else {
				$pos = stripos($query_array[$i], $keys.'=');
				if ($pos === 0) {
					$query_array[$i] = null;
				}
			}
		}
		$size = count($query_array);
		for ($i = 0; $i < $size; $i ++) {
			if (empty($query_array[$i])) {
				unset($query_array[$i]);
			}
		}
		return implode('&', $query_array);
	}
	
	/**
	 * Logging the values. /logs/push_201112.log
	 * @param string $key
	 * @param array $values
	 */
	public static function Logging($key, $values) {
		$log = new BLogger(Yii::app()->basePath . "/logs/push_" . date('Ym') . ".log", BLogger::INFO);
		$log->LogInfo("{{$key}} -> " . json_encode($values));
		if ('error' == strtolower($key)) {
			self::EmailAdmin('ERROR in Server - ' . date('Y-m-d'), print_r($values, 1));
		}
	}
	
	/**
	 * Logging the values. /../APILogs/api_none_201112.log
	 * @param string $key
	 * @param array $values
	 */
	public static function APILogging($key, $values, $prefix = 'none') {
		$prefix = str_replace('/', '_', $prefix);	//base64 chars fix for the invalid file name.
		$log = new BLogger(Yii::app()->basePath . "/../APILogs/api_{$prefix}_" . date('Ym') . ".txt", BLogger::INFO);
		$log->LogInfo("{{$key}} -> " . json_encode($values));
	}
	
	/**
	 * Logging user's action
	 * @param type $userId
	 * @param type $actionId 
	 */
	public static function LoggingAction($userId, $actionId) {
		if (isset($userId) and isset($actionId)) {
			// records user login log
			$connection = Yii::app()->db;
			$command = $connection->createCommand('INSERT INTO tbl_merchant_action_log (id_merchant, action_id) VALUES(:id_merchant, :action_id)');
			$command->execute(array('id_merchant' => $userId, 'action_id' => $actionId));
			$connection->active = false;
		} else throw new CException('userId or actionId is not defined.');
	}
	
	/**
	 * Send mail by Admin.
	 */
	public static function EmailAdmin($subject, $message) {
		if (empty($message)) {
			return true;
		}
		
		//send email to admin mail
		$mailer = Yii::createComponent('application.extensions.mailer.EMailer');
		$mailer->IsSMTP();
		$mailer->Host = 'mail.yamii.fi';
		$mailer->Port = '26';
		$mailer->Username = 'business@yamii.fi';
		$mailer->Password = 'yamii_fi';
		$mailer->SMTPAuth = true;
		$mailer->SetFrom('business@yamii.fi', 'Yamii Debugger');
		$mailer->AddReplyTo('business@yamii.fi', 'Yamii Debugger');
		$mailer->AddAddress('yamii.fi@gmail.com');
		$mailer->Subject = $subject;
		$mailer->MsgHTML($message);
		return $mailer->Send();
	}
	
	/**
	 * Send email to address lists.
	 * 
	 * @param string $subject
	 * @param array $receivers
	 * @param string $htmlText
	 * @param string $plainText
	 * @return boolean
	 */
	public static function SendEmailOut($subject, $receivers, $htmlText, $plainText) {
		if (empty($htmlText)) {
			return true;
		}
		
		//send email from admin mail
		$mailer = Yii::createComponent('application.extensions.mailer.EMailer');
		$mailer->IsSMTP();
		$mailer->Host = 'mail.yamii.fi';
		$mailer->Port = '26';
		$mailer->Username = 'admin@yamii.fi';
		$mailer->Password = 'yamii_fi';
		$mailer->SMTPAuth = true;
		$mailer->SetFrom('business@yamii.fi', 'Yamii for Business');
		$mailer->AddReplyTo('business@yamii.fi', 'Yamii for Business');
		if (is_array($receivers)) {
			foreach ($receivers as $email) {
				$mailer->AddAddress($email);
			}
		} else {
			$mailer->AddAddress($receivers);
		}
		$mailer->CharSet = 'utf-8';
		$mailer->Subject = $subject;
		$mailer->MsgHTML($htmlText);
		if (!empty($plainText)) $mailer->AltBody = $plainText;		
		return $mailer->Send();
	}
	
	/**
	 * IOS push notification.
	 * @param string $push_token - device_notification_token
	 * @param string $messageData['message', 'offer_id']
	 * @throws Exception
	 */
	protected static function _pushNotificationIOS($push_token, $messageData, $isDevp) {
		// Put your private key's passphrase here:
		$passphrase = '1234qwer';//why it's that? bocheng, 2012.12.05
		$pem_file = Yii::app()->basePath . '/data/yamiic_pro.pem';
		if ($isDevp) {
			$pem_file = Yii::app()->basePath . '/data/yamiic_dev.pem';
		}
		
		// Set a context
		$ctx = stream_context_create();
		stream_context_set_option($ctx, 'ssl', 'local_cert', $pem_file);
		stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
		
		// Open a connection to the APNS server
		$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
		if (!$fp) {
			throw new Exception("Failed to connect APNS: {$err} {$errstr}");
		}
		
		// Create the payload body
		$body = array(
			'aps' => array(
				'alert' => $messageData['message'],
				'sound' => 'default',
				'badge' => 1,
			),
			'offer_id' => (string)$messageData['offer_id'],
		);
		
		// Encode the payload as JSON
		$payload = json_encode($body);
		
		// Build the binary notification
		$notification = chr(0) . pack('n', 32) . pack('H*', $push_token) . pack('n', strlen($payload)) . $payload;
		
		// Send it to the server
		$result = fwrite($fp, $notification, strlen($notification));
		if (!$result) {
			throw new Exception("Message not delivered");
		}

		// Close the connection to the server
		fclose($fp);
	}
	
	/**
	 * The following function will send a GCM notification using curl.
	 * 
	 * @param array $registrationIdsArray An array of registration ids to send this notification to
	 * @param array $messageData An named array of data to send as the notification payload
	 * @return type
	 */
	protected static function _pushNotificationAndroidGCM($registrationIdsArray, $messageData) {
		$apiKey = 'AIzaSyDmMjTK6U3waj2uV9uohsMTWfCC1XjTS5Y';
		$headers = array("Content-Type:" . "application/json", "Authorization:" . "key=" . $apiKey);
		$data = array(
			'data' => $messageData,
			'registration_ids' => $registrationIdsArray
		);

		$ch = curl_init();
		curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
		curl_setopt( $ch, CURLOPT_URL, "https://android.googleapis.com/gcm/send" );
		curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
		curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
		curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
		curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode($data) );

		$response = curl_exec($ch);
		curl_close($ch);

		return $response;
	}
	
	/**
	 * Push Notification to iOS/Android(GCM)
	 * 
	 * @param int $id_special offer Id
	 * @param bool $isDevp Is this a development env?
	 */
	public static function PushNotificationToDevices($id_special, $isDevp = false) {
		$connection = Yii::app()->db;
		
		//0. check if this is a good time to push notification
		//if m.status=10000, then push always, otherwise check the time-zone form tbl_sys_timezone (if none, then push)
		$sql_timezone = 'SELECT t.value AS timezone
			FROM tbl_merchant_special s
			INNER JOIN tbl_merchant_account m ON m.id=s.id_merchant
			LEFT JOIN tbl_sys_timezone t ON t.country_code=m.country_code
			WHERE s.id=:id_special ';
		$command = $connection->createCommand($sql_timezone);
		$row = $command->queryRow(true, array(':id_special'=>$id_special));
		if (!empty($row['timezone'])) {
			//check the current time should be between local time (7:00 - 23:00)
			$date = new DateTime('now', new DateTimeZone($row['timezone']));
			$current_hour = $date->format('G');
			if ($current_hour < 7 or $current_hour > 22) {
				//no push notification
				@self::EmailAdmin("No push notification at {$current_hour} on timezone {$row['timezone']}. Push time is 7:00 - 23:00.");
				$connection->active = false;
				return;
			}
		}
		
		//1.find the lat and lng for a special (only for type=1,2: special offers)
		// and check if this offer is active now
		$sql_special = 'SELECT s.id, s.id_merchant, s.content, s.price, s.value, s.discount, s.start_hour, s.end_hour, s.type,
			m.name, m.address, m.postcode, m.city, m.phone, m.url_logo, m.currency_code, m.category_key, m.latitude, m.longitude
			FROM tbl_merchant_special s
			INNER JOIN tbl_merchant_account m ON m.id = s.id_merchant
			WHERE s.id=:id AND s.type IN (1,2) AND s.status=1 AND s.trash=0 AND s.start_date<NOW() AND s.end_date>NOW()
			  AND s.week_days LIKE :week_days 
			  AND ( 
				        (s.start_hour<s.end_hour AND s.start_hour<=:hour_min AND s.end_hour>=:hour_min)
					 OR (s.start_hour>s.end_hour AND s.start_hour<=:hour_min) 
					 OR (s.start_hour>s.end_hour AND s.start_hour>:hour_min AND s.end_hour>=:hour_min) 
			  )';
		$params = array(
			':id' => $id_special,
			':week_days' => '%'.date('N').'%',
			':hour_min' => (int)date('Gi'),
		);
		$command = $connection->createCommand($sql_special);
		$row = $command->queryRow(true, $params);
		if (!empty($row['id'])) {
			//2.find all devices which should be notified
			$sql_devices = 'SELECT DISTINCT
				d.device_token, d.device_type, d.device_notification_token, d.id_user, d.notified_time, d.latitude, d.longitude, d.min_discount, d.max_distance, d.lang,
				f.id_merchant,
				(6371 * acos(cos(radians(:lat)) * cos(radians(d.latitude)) * cos(radians(d.longitude) - radians(:lng)) + sin(radians(:lat)) * sin(radians(d.latitude)))) AS distance
				FROM tbl_user_device_settings d
				LEFT JOIN tbl_user_follow_merchant f ON d.id_user=f.id_user AND f.id_merchant=:id_merchant
				WHERE ( (6371 * acos(cos(radians(:lat)) * cos(radians(d.latitude)) * cos(radians(d.longitude) - radians(:lng)) + sin(radians(:lat)) * sin(radians(d.latitude))))<d.max_distance AND (d.min_discount-0.01)<:discount )
				   OR (f.id_merchant=:id_merchant)
				ORDER BY d.insert_time DESC';
			//距离和折扣满足AND条件
			$command = $connection->createCommand($sql_devices);
			$devices = $command->queryAll(true, array(
				':lat' => $row['latitude'],
				':lng' => $row['longitude'],
				':id_merchant' => $row['id_merchant'],
				':discount' => 1 - ((float)$row['price']/(float)$row['value']),
			));
			
			//make message
			$tmpl['fi'] = 'Uusi tarjous löytyy: {merchant_name}, {discount}, ({distance}).';
			$tmpl['en'] = 'New offers found: {discount} at {merchant_name} ({distance}).';
			$discount = 1 - ((float)$row['price']/(float)$row['value']);
			$params = array(
				'{discount}' => '-' . (round($discount, 2) * 100) . '%',
				'{merchant_name}' => $row['name'],
			);
			
			//looping for all devices
			$loged_push = '';
			$loged_push_response = '';
			foreach ($devices as $k => $v) {
				//check the distance per device
				$params['{distance}'] = round($v['distance'], 1) . 'km';
				if ($v['distance'] < 1) {
					$params['{distance}'] = round($v['distance']*1000, 0) . 'm';
				}
				
				//check the langauge per device
				switch (strtolower($v['lang'])) {
					case 'fi':
					case 'fi_fi':
						$message = str_replace(array_keys($params), array_values($params), $tmpl['fi']);
						$lang = 'fi';
						break;
					default:
						$message = str_replace(array_keys($params), array_values($params), $tmpl['en']);
						$lang = 'en';
						break;
				}
				
				//make the messageData
				$messageData = array(
					'message' => $message,
					'offer_id' => $id_special,
				);
				
				//start to push messages to devices
				if ($v['device_type'] == 'ios') {
					//push notification to iOS devices one by one
					if (!empty($v['device_notification_token'])) {
						self::_pushNotificationIOS($v['device_notification_token'], $messageData, $isDevp);
					}
				} elseif ($v['device_type'] == 'android') {
					//push notification to Android GCM one by one
					if (!empty($v['device_notification_token'])) {
						$response = self::_pushNotificationAndroidGCM(array($v['device_notification_token']), $messageData);
						$loged_push_response .= $response . "\n";
					}
				}
				
				//logging
				if (!empty($v['device_notification_token'])) {
					$loged_push .= "[{$k}]: [{$v['device_token']}] [{$v['device_type']}], {$v['device_notification_token']}, {$v['id_user']}, {$v['min_discount']}, {$v['max_distance']}, {$v['distance']}, [{$v['id_merchant']}]\n";
					
					// save to table: tbl_user_device_pushing_log, 2012.12.28, Bocheng
					// only for ios device
					$sql_pushing_log = 'INSERT INTO tbl_user_device_pushing_log (device_token, device_type, id_special, id_user, distance)
						VALUES(:device_token, :device_type, :id_special, :id_user, :distance)';
					$params_pushing = array(
						':device_token' => $v['device_token'],
						':device_type' => $v['device_type'],
						':id_special' => $id_special,
						':id_user' => $v['id_user'],
						':distance' => $v['distance'],
					);
					$command = $connection->createCommand($sql_pushing_log);
					$command->execute($params_pushing);
				}
			}
			
			//the email content "$loged_push" cannot be empty. otherwise, error comes!
			if (!empty($loged_push)) {
				@self::EmailAdmin('BUtils::PushNotificationToDevices at ' . date('Y-m-d H:i:s') . ' for special ' . $id_special, $loged_push . "\n" . $loged_push_response);
			}
		}
		$connection->active = false;
	}
	
	/**
	 * Push Notification to one device iOS/Android(GCM)
	 * 
	 * @param string $push_token - device_notification_token
	 * @param string $device_type - ios/android/wp
	 * @param array $messageData
	 * @param bool $isDevp
	 * @return int 1 - success, 0 - failed
	 */
	public static function PushNotificationToDevice($push_token, $device_type, $messageData, $isDevp = false) {
		$count = 0;
		
		//start to push messages to devices
		if ($device_type == 'ios') {
			self::_pushNotificationIOS($push_token, $messageData, $isDevp);
			$count = 1;
		} elseif ($device_type == 'android') {
			$response = self::_pushNotificationAndroidGCM(array($push_token), $messageData);
			$json_array = json_decode($response, true);
			if ($json_array['success'] == 1) {
				$count = 1;
			} else {
				self::APILogging('notification_failed', $response);
			}
		}
		
		return $count;
	}
}